#define LOG_TAG "dataflow"
#include "wa_edge_client_api.h"


struct WA_DATA_MONITOR_TAG 
{
    int mon_id;  //returned by wa-iot-edge
    bool global;
    bool process;
    bool registered;
    char * device_id;
    char * res_id;
    char * app_id;
    char * URI;
    char * tag;
    int internval_ms;
    int sequence;
    WA_Resource_Handler data_handler;
    WA_EDGE_CONTEXT iot_edge;
};


WA_DATA_MONITOR wa_monitor_global_new(WA_EDGE_CONTEXT iot_edge, const char * my_app_id, const char * tag, WA_Resource_Handler data_handler)
{
    WA_DATA_MONITOR monitor = (WA_DATA_MONITOR) malloc_z(sizeof(*monitor));
    if(monitor == NULL) return NULL;

    monitor->mon_id = -1;
    monitor->internval_ms = 30 * 1000;
    monitor->global = true;
    monitor->iot_edge = iot_edge;
    monitor->data_handler = data_handler;
    monitor->app_id = strdup(my_app_id);
    if(tag) monitor->tag = strdup(tag);
    return monitor;
}

WA_DATA_MONITOR wa_monitor_res_new(WA_EDGE_CONTEXT iot_edge, const char * my_app_id, const char * tag, const char * di, const char * ri, WA_Resource_Handler data_handler)
{
    WA_DATA_MONITOR monitor = wa_monitor_global_new(iot_edge, my_app_id, tag, data_handler);
    if(monitor == NULL) return NULL;
    monitor->global = false;
    monitor->device_id = strdup(di);
    monitor->res_id = strdup(ri);
    return monitor;
}


void wa_monitor_set_interval_ms(WA_DATA_MONITOR monitor, int ms)
{
    monitor->internval_ms = ms;
}

void wa_monitor_set_process(WA_DATA_MONITOR monitor, bool process)
{
    monitor->process = process;
}

void wa_monitor_set_sequence(WA_DATA_MONITOR monitor, int sequence)
{
    monitor->sequence = sequence;
}

bool wa_monitor_run(WA_DATA_MONITOR monitor)
{
    bool ret = false;
    RESTFUL_CONTEXT restful_context = wa_wagent_get_restful_context(monitor->iot_edge);
    coap_context_t * coap_ctx = wa_coap_get_context(restful_context);
    cJSON * root = cJSON_CreateObject();
    if(!monitor->global)
    {
        cJSON_AddStringToObject(root, "di", monitor->device_id);
        cJSON_AddStringToObject(root, "ri", monitor->res_id);
    }
    cJSON_AddStringToObject(root, "app-id", monitor->app_id);
    if(monitor->URI == NULL)
    {
        monitor->URI = (char*) malloc_z(40);
        snprintf(monitor->URI, 40, "%u", rand());
    }
    cJSON_AddStringToObject(root, "pub-addr", monitor->URI);
    cJSON_AddBoolToObject(root, "process", monitor->process);
    cJSON_AddNumberToObject(root, "sequence", monitor->sequence);
    cJSON_AddNumberToObject(root, "interval_ms", monitor->internval_ms);
    if(monitor->tag)
        cJSON_AddStringToObject(root, "monitor-tag", monitor->tag);

    // wa_wagent_request use a temp port for sending request, 
    // so we explicilty tell the port for reciving the data notification.
    if(coap_ctx->my_addr.port)
        cJSON_AddNumberToObject(root, "port", coap_ctx->my_addr.port);

    char * payload = cJSON_Print(root);
    cJSON_Delete(root);

    restful_response_t * response = wa_wagent_request(monitor->iot_edge, T_Post, (char*)"refresher", NULL, payload, IA_APPLICATION_JSON);
    if(response && response->code == CREATED_2_01 && response->payload_len > 0)
    {
        monitor->mon_id = atoi(response->payload);
        WALOG("Created monitor id: %d", monitor->mon_id);
        ret = true;
    }

    if(!monitor->registered)
    {
        wa_coap_register_resource(restful_context, monitor->URI, monitor->data_handler, T_Default);
        monitor->registered = true;
    }

    if(response) wa_free_response(response);
    cJSON_free(payload);
    return ret;
}

static void wa_monitor_destory(WA_DATA_MONITOR mon)
{
    if(mon->app_id) free(mon->app_id);
    if(mon->device_id) free(mon->device_id);
    if(mon->res_id) free(mon->res_id);
    if(mon->URI) free(mon->URI);
    free(mon);
}

void wa_monitor_delete(WA_DATA_MONITOR monitor)
{
    if(monitor->mon_id != -1)
    {
        char query[100];
        snprintf(query, sizeof(query), "mid=%u", monitor->mon_id);
        if(!wa_wagent_simple_request(monitor->iot_edge, T_Del, (char*)"refresher", query, NULL, IA_TEXT_PLAIN))
            WALOG("delete monitor id %d fail", monitor->mon_id);
    }

    wa_monitor_destory(monitor);
}


void wa_monitor_free_notification(wa_data_notify_t * notify)
{
    if(notify->device_id) free(notify->device_id);
    if(notify->property_name) free(notify->property_name);
    if(notify->resource_id) free(notify->resource_id);
    if(notify->tag) free(notify->tag);
    free(notify);
}

wa_data_notify_t * wa_monitor_parse_notification_A(restful_request_t * request)
{
    if(request->query == NULL) return NULL;
    char buf[256] = {0};
    char * query = request->query;
    int len = strlen(query);
    if(!find_key_value(query, len, "di", buf, sizeof(buf), '&'))
        return NULL;

    wa_data_notify_t * n = (wa_data_notify_t *) malloc_z(sizeof(wa_data_notify_t));
    if(n == NULL) return NULL;
    n->device_id = strdup(buf);
    memset(buf, 0, sizeof(buf));
    if(!find_key_value(query, len, "ri", buf, sizeof(buf), '&'))
    {
        goto fail;
    }
    n->resource_id = strdup(buf);
    memset(buf, 0, sizeof(buf));
    if(find_key_value(query, len, "pt", buf, sizeof(buf), '&'))
    {
        n->property_name = strdup(buf);
    }
    memset(buf, 0, sizeof(buf));
    if(find_key_value(query, len, "tag", buf, sizeof(buf), '&'))
    {
        n->tag = strdup(buf);
    }

    n->require_response = (request->action == T_Post);
    return n;

fail:
    wa_monitor_free_notification(n);
    return NULL;
}


int wa_monitor_get_decision_code(wa_process_decision_t decision)
{
    int code;
    if(decision == WA_Process_Drop)
        code = FORBIDDEN_4_03;
    else  if(decision == WA_Process_Modified)
        code = CHANGED_2_04;
    else if(decision == WA_Process_Continue)
        code = CONTINUE_2_31;
    else
        code = MANUAL_RESPONSE;
    return code;
}


void wa_monitor_set_decision(REQ_ENV_HANDLE req_env, wa_process_decision_t decision)
{
    wa_set_response_status(req_env, wa_monitor_get_decision_code(decision));
}